<?php
/**
 * QrCodeRepo.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-02-14 02:08:18
 * @modified    2023-02-14 02:08:18
 *
 */

namespace App\Repositories\Product;

use App\Models\Product;
use App\Models\Product\QrCode;
use Darabonba\GatewaySpi\Models\InterceptorContext\request;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
use Endroid\QrCode\Label\Alignment\LabelAlignmentCenter;
use Endroid\QrCode\Label\Font\NotoSans;
use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
use Endroid\QrCode\Writer\PngWriter;
use Illuminate\Support\Str;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use SimpleSoftwareIO\QrCode\Facades\QrCode as SimpleQrCode;
use ZipStream\ZipStream;

class QrCodeRepo
{
    const QrCodePath = 'qr_codes/';
    const FILE_TYPE = 'png';

    const LABEL_PREFIX = '嘉盛售后 扫码报修';

    public static function getList()
    {
        $perPage = (int)request('limit');
        if ($perPage < 1) {
            $perPage = 1;
        }

        $productId = intval(request()->get('product_id') ?? 0);
        $productName = trim(request()->get('product_name') ?? '');
        $code = trim(request()->get('code') ?? '');

        $builder = QrCode::query();
        $builder->select(['product_qrcode.*']);
        $builder->orderByDesc('product_qrcode.id');
        $builder->with('product');

        if ($productId || $productName) {
            $builder->join('product', 'product.id', '=', 'product_qrcode.product_id');
            $builder->where(function ($query) use ($productId, $productName) {
                if ($productId) {
                    $query->where('product.id', $productId);
                }
                if ($productName) {
                    $query->where('product.name', 'like', "{$productName}");
                }
            });
        }

        if ($code) {
            $builder->where('product_qrcode.code', 'like', "%{$code}%");
        }
        return $builder->paginate($perPage);
    }

    public static function validateProduct($id, $data)
    {
        $productId = $data['product_id'];
        if (!$productId) {
            return;
        }
        $qrCodeProduct = QrCode::byProductId($productId)->first();
        if (!$qrCodeProduct) {
            return;
        }
        // 新增: 商品已绑定二维码
        if (!$id && $qrCodeProduct) {
            throw new \Exception('该商品已绑定二维码，请先解绑');
        }
        // 修改: id和二维码id是否一致
        if ($id && $qrCodeProduct && $id != $qrCodeProduct->id) {
            throw new \Exception('该商品已绑定二维码，请先解绑');
        }
    }

    public static function generate($count)
    {
        if ($count <= 0) {
            throw new \Exception('数量需要大于0');
        }
        if ($count > 100) {
            $count = 100;
        }
        for ($i = 0; $i < $count; $i++) {
            self::store(0);
        }
    }

    public static function search()
    {
        $code = trim(request()->get('code'));
        $filterBinded = request()->get('filter_binded');

        $builder = QrCode::query();
        if ($code) {
            $builder->where('code', 'like', "%{$code}%");
        }

        if (!is_null($filterBinded)) {
            $filterBinded = intval($filterBinded);
            if ($filterBinded) {
                $builder->where('product_id', 0);
            } else {
                $builder->where('product_id', '<>', 0);
            }
        }

        $builder->orderByDesc('id')
            ->limit(10);
        return $builder->get();
    }

    public static function store($id, $qrCodeData = [])
    {
        $path = self::generateQrCodeImage();
        if (!$qrCodeData) {
            $data = self::generateData();
        } else {
            $data = $qrCodeData;
        }
        self::validateProduct($id, $data);
        $data['path'] = $path;
        if ($id) {
            $qrCode = QrCode::find($id);
        } else {
            $qrCode = new QrCode();
        }
        if (empty($qrCode)){
            $qrCode = new QrCode();
        }

        $productId = $data['product_id'] ?? '';

        $data['code'] = self::generateQrcode($id, $productId);
        $qrCode->fill($data);
        $qrCode->saveOrFail();
        self::writeDataToQrCode($qrCode->id, $path);

        return self::fetch($qrCode->id);
    }

    public static function fetch($id)
    {
        $qrCode = QrCode::find($id);
        if (empty($qrCode)) {
            throw new \Exception('二维码不存在');
        }
        return $qrCode;
    }

    public static function destroy($id)
    {
        $qrCode = QrCode::find($id);
        if (empty($qrCode)) {
            throw new \Exception('二维码不存在');
        }
        $qrCodePath = public_path() . '/' . $qrCode->path;
        if (is_file($qrCodePath)) {
            @unlink($qrCodePath);
        }
        $qrCode->delete();
    }

    public static function generateQrCodeImage(): string
    {
        $path = self::QrCodePath . date('Y/m/d/');
        $absolutePath = public_path() . '/' . $path;
        $fileName = Str::random(64) . '.' . self::FILE_TYPE;
        $returnPath = $path . $fileName;
        if (!is_dir($absolutePath)) {
            @mkdir($absolutePath, 0777, true);
        }

        $absoluteImage = $absolutePath . $fileName;
        if (!is_file($absoluteImage)) {
            fopen($absoluteImage, 'w');
        }
        return $returnPath;
    }

    /**
     * 导出特定选择的二维码
     */
    public static function export_special()
    {
        $ids = self::getExportIds();
        if (!$ids) {
            throw new \Exception('请选择要导出的二维码');
        }
        $url = self::export($ids);
        return $url;
    }

    /**
     * 根据ID导出二维码
     */
    private static function export($ids)
    {
        $remotePathPrefix = 'qrcode_zip/' . date('Ymd');
        $pathPrefix = public_path($remotePathPrefix);
        if (!is_dir($pathPrefix)) {
            mkdir($pathPrefix, 0777, true);
        }
        // 删除原有二维码和文件
        if ($dh = opendir($pathPrefix)) {
            while (($file = readdir($dh)) !== false) {
                if (Str::startsWith($file, '.')) {
                    continue;
                }
                if (Str::endsWith($file, '.zip')) {
                    continue;
                }
                @unlink($pathPrefix . '/' . $file);
            }
        }
        $qrCodes = QrCode::with(['product.customer'])->byIds($ids)->get();
        $qrCodeProducts = [];
        $qrCodeProducts[] = [
            '序号',
            '二维码编号',
            '二维码状态',
            '关联产品',
            '产品编号',
            '客户名称',
            '安装位置'
        ];
        foreach ($qrCodes as $key => $qrCode) {
            $qrCodeCode = $qrCode->code;
            $qrCodeProduct = $qrCode->product;
            if (!empty($qrCodeProduct)) {
                $status = '已关联';
                $productName = $qrCodeProduct->name;
                $productNumber = $qrCodeProduct->product_number;
                $provinceName = $qrCodeProduct->province_name ?? '';
                $cityName = $qrCodeProduct->city_name ?? '';
                $countyName = $qrCodeProduct->county_name ?? '';
                $installAddress = $qrCodeProduct->install_address ?? '';
                $productInstallAddress = $provinceName . $cityName . $countyName . $installAddress;
            } else {
                $status = '未关联';
                $productNumber = '';
                $productName = '';
                $productInstallAddress = '';
            }
            $index = $key + 1;
            $temp = [
                ' ' . $index,
                ' ' . strval($qrCodeCode),
                ' ' . $status,
                ' ' . $productName,
                ' ' . $productNumber,
                ' ' . $qrCode->product->customer->name ?? '',
                ' ' . $productInstallAddress
            ];
            $qrCodeProducts[] = $temp;
        }
        // 转移文件
        foreach ($qrCodes as $qrCode) {
            $itemPath = $qrCode->path;
            $itemCode = $qrCode->code;
            $itemSourcePath = public_path() . '/' . $itemPath;
            $itemSourcePathArr = explode('.', $itemSourcePath);
            $fileType = $itemSourcePathArr[count($itemSourcePathArr) - 1];
            $filePath = $pathPrefix . '/' . $itemCode . '.' . $fileType;
            copy($itemSourcePath, $filePath);
            $zipQrCodeFiles[] = $filePath;
        }

        $namePrefix = date('YmdHis') . rand(10000, 99999);
        $zipRemoteFilePath = '/qrcode-' . $namePrefix . '.zip';
        $zipFilePath = $pathPrefix . $zipRemoteFilePath;
        $excelFile1 = $pathPrefix . '/product-qrcode-' . $namePrefix . '.xlsx';
        $zip = new \ZipArchive();
        if ($zip->open($zipFilePath, \ZipArchive::CREATE) !== true) {
            throw new \Exception('无法打开文件或创建压缩包失败');
        }
        $remoteZipUrl = env('APP_URL') . '/' . $remotePathPrefix . $zipRemoteFilePath;
        $qrSpreadsheet = new Spreadsheet();
        $qrSpreadsheet->removeSheetByIndex(0);
        $qrSheet1 = new Worksheet($qrSpreadsheet, '产品二维码');
        $qrSpreadsheet->addSheet($qrSheet1, 0);
        $qrSheet1->fromArray($qrCodeProducts);
        $writer = new Xlsx($qrSpreadsheet);
        $writer->save($excelFile1);

        $zip->addFile($excelFile1, basename($excelFile1));
        foreach ($zipQrCodeFiles as $zipQrCodeFile) {
            $zip->addFile($zipQrCodeFile, basename($zipQrCodeFile));
        }
        $zip->close();
        if (!file_exists($zipFilePath)) {
            throw new \Exception('压缩包不存在');
        }
//        header('Cache-Control: public');
//        header('Content-Description: File Transfer');
//        header('Content-disposition: attachment; filename=' . basename($zipFilePath));
//        header('Content-Type: application/zip');
//        header('Content-Transfer-Encoding: binary');
//        header('Content-Length: ' . filesize($zipFilePath));
        // @readfile($zipFilePath);
        return $remoteZipUrl;
    }

    private static function getExportIds()
    {
        $idsQuery = request()->get('ids');
        $idsQueryArr = explode(',', $idsQuery);
        $idsArr = [];
        foreach ($idsQueryArr as $item) {
            $itemId = intval(trim($item));
            if ($itemId <= 0) {
                continue;
            }
            $itemQrcode = QrCode::find($itemId);
            if (empty($itemQrcode)) {
                continue;
            }
            $idsArr[] = $itemId;
        }
        return array_unique($idsArr);
    }

    private static function writeDataToQrCode($id, $path)
    {
        $qrCode = QrCode::find($id);
        if (empty($qrCode)) {
            throw new \Exception('二维码不存在');
        }
        $code = $qrCode->code;
        $absolutePath = public_path() . '/' . $path;
        /**
         * $url = 'http://api.josen.test/qrcode?qrcode_id=7';
         * json_encode($data) 用 url 替换，即可实现跳转
         */
        $url = env('QRCODE_SCAN_URL') . '/#/home/' . $id;
        $result = Builder::create()
            ->writer(new PngWriter())
            ->data($url)
            ->encoding(new Encoding('UTF-8'))
            ->errorCorrectionLevel(new ErrorCorrectionLevelHigh())
            ->size(400)
            ->margin(10)
            ->roundBlockSizeMode(new RoundBlockSizeModeMargin())
            ->logoPath(resource_path('imgs/') . 'logo-white.png')
            ->logoResizeToWidth(80)
            ->logoResizeToHeight(80)
            ->labelText( self::LABEL_PREFIX . ' ' . $code)
            ->labelFont(new NotoSans(12))
            ->labelAlignment(new LabelAlignmentCenter())
            ->validateResult(false)
            ->build();
        $result->saveToFile($absolutePath);
    }

    private static function generateData()
    {
        $qrCodeData = request()->getContent();
        $qrCodeArr = json_decode($qrCodeData, true);
        $productId = intval($qrCodeArr['product_id'] ?? 0);
        return [
            'product_id' => $productId,
        ];
    }

    /**
     * 生成二维码code
     *
     * @param $id
     * @param $productId
     * @return string
     */
    private static function generateQrcode($id, $productId)
    {
        $product = Product::find($productId);

        $departmentId = $product->department_id ?? 0;
        $customerId = $product->customer_id ?? 0;

        $code = "{$departmentId}-{$customerId}-" . date('ymd') . rand(1000, 9999);
        if ($id) {
            $qrcode = QrCode::find($id);
            if ($qrcode) {
                return $qrcode->code;
            }
        }

        $codeQrcode = QrCode::byCode($code)->first();
        if (empty($codeQrcode)) {
            return $code;
        }
        return self::generateQrcode($id, $productId);
    }
}